import { Injectable, Logger, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import * as CircuitBreaker from 'opossum';
import * as fs from 'fs/promises';
import * as FormData from 'form-data';

// 异常
import {
  AiInferenceException,
  ExceptionUtils,
  ErrorCode,
} from './exceptions/scoring.exceptions';

/**
 * AI推理服务 - 零隐患MFIS规范实现
 * 解决隐患：#3 AI服务缺乏熔断机制，#10 数据库重试策略不当，#14 日志信息泄露
 */
@Injectable()
export class AiInferenceService {
  private readonly logger = new Logger(AiInferenceService.name);
  private readonly circuitBreaker: CircuitBreaker;
  private readonly aiServiceUrl: string;
  private readonly timeout: number;

  constructor(
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
    @Inject('AI_CIRCUIT_BREAKER') circuitBreaker: any,
  ) {
    this.aiServiceUrl = this.configService.get<string>('AI_INFERENCE_URL');
    this.timeout = this.configService.get<number>('AI_SERVICE_TIMEOUT', 10000);

    // 隐患 #3 解决方案：配置熔断器
    this.circuitBreaker = circuitBreaker;
    this.configureCircuitBreaker();
  }

  /**
   * 配置熔断器
   * 解决隐患：#3 AI服务熔断机制
   */
  private configureCircuitBreaker(): void {
    // 重置熔断器的执行函数
    this.circuitBreaker.fallback = this.createFallbackResponse();
    this.circuitBreaker.options.timeout = this.timeout;

    // 熔断器事件监听
    this.circuitBreaker.on('open', () => {
      this.logger.warn('🚨 AI服务熔断器已开启 - 服务降级模式');
      this.logCircuitBreakerStats();
    });

    this.circuitBreaker.on('halfOpen', () => {
      this.logger.info('🔄 AI服务熔断器半开状态 - 尝试恢复');
    });

    this.circuitBreaker.on('close', () => {
      this.logger.info('✅ AI服务熔断器已关闭 - 服务恢复正常');
      this.logCircuitBreakerStats();
    });

    this.circuitBreaker.on('fallback', (result) => {
      this.logger.warn('AI服务调用降级', {
        result: typeof result === 'string' ? result.substring(0, 100) : 'fallback_result'
      });
    });

    this.circuitBreaker.on('reject', () => {
      this.logger.error('AI服务调用被熔断器拒绝');
    });

    this.circuitBreaker.on('timeout', () => {
      this.logger.error('AI服务调用超时');
    });

    this.logger.log('🔧 AI熔断器配置完成', {
      timeout: this.timeout,
      errorThreshold: this.circuitBreaker.options.errorThresholdPercentage,
      resetTimeout: this.circuitBreaker.options.resetTimeout,
    });
  }

  /**
   * 调用AI推理服务
   * 解决隐患：#3 熔断器保护，#14 日志脱敏
   */
  async callInference(imagePath: string): Promise<any> {
    const correlationId = this.generateCorrelationId();

    this.logger.debug('开始AI推理调用', {
      correlation_id: correlationId,
      image_path: this.sanitizePath(imagePath),
      service_url: this.aiServiceUrl,
    });

    try {
      // 检查熔断器状态
      if (this.circuitBreaker.opened) {
        throw new AiInferenceException('AI服务熔断器已开启，服务暂时不可用');
      }

      // 验证图像文件存在
      await this.validateImageFile(imagePath);

      // 调用受熔断器保护的AI服务
      const result = await this.circuitBreaker.fire(imagePath);

      // 验证返回结果
      this.validateInferenceResult(result);

      this.logger.debug('AI推理成功', {
        correlation_id: correlationId,
        score: result.score,
        confidence: result.confidence,
        inference_time: result.inference_time,
      });

      return {
        ...result,
        correlation_id: correlationId,
        service_url: this.aiServiceUrl,
        timestamp: new Date().toISOString(),
      };

    } catch (error) {
      this.logger.error('AI推理调用失败', {
        correlation_id: correlationId,
        image_path: this.sanitizePath(imagePath),
        error: ExceptionUtils.createUserFriendlyResponse(error),
        circuit_breaker_state: this.getCircuitBreakerState(),
      });

      if (error instanceof AiInferenceException) {
        throw error;
      }

      // 根据错误类型抛出相应的异常
      if (error.code === 'ENOENT') {
        throw new AiInferenceException('图像文件不存在');
      }

      if (error.code === 'ECONNREFUSED') {
        throw new AiInferenceException('AI服务连接失败');
      }

      if (error.name === 'TimeoutError') {
        throw new AiInferenceException('AI服务响应超时');
      }

      throw new AiInferenceException('AI推理服务调用失败', error);
    }
  }

  /**
   * 实际的AI服务调用
   * 解决隐患：#10 重试策略（由熔断器处理）
   */
  private async callAiService(imagePath: string): Promise<any> {
    const startTime = Date.now();

    try {
      // 验证图像文件
      const fileStats = await fs.stat(imagePath);
      if (fileStats.size === 0) {
        throw new Error('图像文件为空');
      }

      // 准备表单数据
      const formData = new FormData();
      formData.append('file', await fs.readFile(imagePath), {
        filename: this.extractFilename(imagePath),
        contentType: this.getContentType(imagePath),
      });

      // 添加元数据
      formData.append('request_id', this.generateCorrelationId());
      formData.append('model_version', 'latest');
      formData.append('confidence_threshold', '0.5');

      // 发送请求
      const response = await firstValueFrom(
        this.httpService.post(this.aiServiceUrl, formData, {
          headers: {
            ...formData.getHeaders(),
            'Content-Type': 'multipart/form-data',
            'X-Request-ID': this.generateCorrelationId(),
            'User-Agent': 'UC-System-NestJS/2.0.0',
          },
          timeout: this.timeout,
          maxRedirects: 3,
          validateStatus: (status) => status === 200,
        })
      );

      const inferenceTime = Date.now() - startTime;

      // 验证响应结构
      if (!response.data) {
        throw new Error('AI服务返回空响应');
      }

      return {
        ...response.data,
        inference_time: inferenceTime,
        request_timestamp: new Date().toISOString(),
      };

    } catch (error) {
      // 处理HTTP错误
      if (error.response) {
        const status = error.response.status;
        const message = error.response.data?.message || 'AI服务返回错误';

        switch (status) {
          case 400:
            throw new AiInferenceException(`请求参数错误: ${message}`);
          case 401:
            throw new AiInferenceException('AI服务认证失败');
          case 403:
            throw new AiInferenceException('AI服务访问被拒绝');
          case 429:
            throw new AiInferenceException('AI服务请求过于频繁');
          case 500:
            throw new AiInferenceException('AI服务内部错误');
          case 502:
          case 503:
          case 504:
            throw new AiInferenceException('AI服务暂时不可用');
          default:
            throw new AiInferenceException(`AI服务返回错误: ${status} ${message}`);
        }
      }

      // 网络错误
      if (error.code === 'ECONNREFUSED') {
        throw new AiInferenceException('无法连接到AI服务');
      }

      if (error.code === 'ETIMEDOUT' || error.name === 'TimeoutError') {
        throw new AiInferenceException('AI服务请求超时');
      }

      throw error;
    }
  }

  /**
   * 创建降级响应
   */
  private createFallbackResponse(): (imagePath: string) => Promise<any> {
    return async (imagePath: string): Promise<any> => {
      this.logger.warn('AI服务降级响应', {
        image_path: this.sanitizePath(imagePath),
        fallback_reason: 'circuit_breaker_open',
      });

      throw new AiInferenceException(
        'AI推理服务暂时不可用，请稍后重试。系统正在自动恢复中。',
        new Error('Circuit breaker fallback')
      );
    };
  }

  /**
   * 验证图像文件
   */
  private async validateImageFile(imagePath: string): Promise<void> {
    try {
      const stats = await fs.stat(imagePath);

      // 检查文件大小
      const maxSize = this.configService.get<number>('MAX_FILE_SIZE', 10 * 1024 * 1024);
      if (stats.size > maxSize) {
        throw new AiInferenceException(`图像文件过大，最大允许 ${maxSize / 1024 / 1024}MB`);
      }

      if (stats.size === 0) {
        throw new AiInferenceException('图像文件为空');
      }

      // 检查文件扩展名
      const allowedExtensions = ['.jpg', '.jpeg', '.png', '.tiff', '.bmp'];
      const fileExtension = imagePath.toLowerCase().substring(imagePath.lastIndexOf('.'));

      if (!allowedExtensions.includes(fileExtension)) {
        throw new AiInferenceException(`不支持的图像格式: ${fileExtension}`);
      }

    } catch (error) {
      if (error.code === 'ENOENT') {
        throw new AiInferenceException('图像文件不存在');
      }

      if (error instanceof AiInferenceException) {
        throw error;
      }

      throw new AiInferenceException('图像文件验证失败', error);
    }
  }

  /**
   * 验证推理结果
   */
  private validateInferenceResult(result: any): void {
    if (!result || typeof result !== 'object') {
      throw new AiInferenceException('AI服务返回无效的响应格式');
    }

    const requiredFields = ['score', 'severity', 'confidence', 'model_version'];
    const missingFields = requiredFields.filter(field => !(field in result));

    if (missingFields.length > 0) {
      throw new AiInferenceException(`AI响应缺少必需字段: ${missingFields.join(', ')}`);
    }

    // 验证数据类型和范围
    if (typeof result.score !== 'number' || result.score < 0 || result.score > 8) {
      throw new AiInferenceException('AI评分值无效，应在0-8范围内');
    }

    if (typeof result.confidence !== 'number' || result.confidence < 0 || result.confidence > 1) {
      throw new AiInferenceException('AI置信度值无效，应在0-1范围内');
    }

    if (!result.severity || typeof result.severity !== 'string') {
      throw new AiInferenceException('AI严重程度值无效');
    }
  }

  /**
   * 获取熔断器状态
   */
  private getCircuitBreakerState(): any {
    return {
      state: this.circuitBreaker.state,
      stats: this.circuitBreaker.stats,
      opened: this.circuitBreaker.opened,
    };
  }

  /**
   * 记录熔断器统计信息
   */
  private logCircuitBreakerStats(): void {
    const stats = this.circuitBreaker.stats;
    this.logger.info('熔断器统计信息', {
      state: this.circuitBreaker.state,
      fires: stats.fires,
      successes: stats.successes,
      failures: stats.failures,
      rejects: stats.rejects,
      timeouts: stats.timeouts,
      fallbacks: stats.fallbacks,
      successRate: stats.fires > 0 ? (stats.successes / stats.fires * 100).toFixed(2) + '%' : 'N/A',
    });
  }

  /**
   * 工具方法
   */
  private generateCorrelationId(): string {
    return `AI-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }

  private sanitizePath(filePath: string): string {
    if (!filePath) return '';
    return filePath.replace(/[A-Za-z]:\\/g, '[驱动器]:\\').replace(/[^/]+$/, '[文件名]');
  }

  private extractFilename(filePath: string): string {
    return filePath.split(/[\\/]/).pop() || 'image.jpg';
  }

  private getContentType(filePath: string): string {
    const extension = filePath.toLowerCase().split('.').pop();
    const contentTypes = {
      'jpg': 'image/jpeg',
      'jpeg': 'image/jpeg',
      'png': 'image/png',
      'tiff': 'image/tiff',
      'bmp': 'image/bmp',
    };
    return contentTypes[extension] || 'image/jpeg';
  }

  /**
   * 健康检查
   */
  async healthCheck(): Promise<any> {
    return {
      service: 'ai-inference',
      status: this.circuitBreaker.opened ? 'degraded' : 'healthy',
      circuit_breaker: this.getCircuitBreakerState(),
      service_url: this.aiServiceUrl,
      timeout: this.timeout,
      timestamp: new Date().toISOString(),
    };
  }

  /**
   * 手动重置熔断器
   */
  resetCircuitBreaker(): void {
    this.circuitBreaker.open();
    setTimeout(() => {
      this.circuitBreaker.close();
    }, 1000);

    this.logger.info('熔断器已手动重置');
  }
}